package cn.iocoder.yudao.module.system.service.useractivity;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
// 移除对infra模块内部类的直接依赖
import cn.iocoder.yudao.module.system.dal.dataobject.logger.LoginLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.dataobject.useractivity.UserActivityStatsDO;
import cn.iocoder.yudao.module.system.dal.dataobject.useractivity.UserDailyActivityDO;
import cn.iocoder.yudao.module.system.dal.mysql.logger.LoginLogMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.dal.mysql.useractivity.UserActivityStatsMapper;
import cn.iocoder.yudao.module.system.dal.mysql.useractivity.UserDailyActivityMapper;
import cn.iocoder.yudao.module.system.enums.logger.LoginResultEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 用户活跃度统计 Service 实现类
 *
 * @author 系统开发团队
 */
@Service
@Slf4j
public class UserActivityStatsServiceImpl implements UserActivityStatsService {

    @Resource
    private UserActivityStatsMapper userActivityStatsMapper;
    @Resource
    private UserDailyActivityMapper userDailyActivityMapper;
    @Resource
    private LoginLogMapper loginLogMapper;
    // 移除对ApiAccessLogMapper的直接依赖
    @Resource
    private AdminUserMapper adminUserMapper;
    
    /**
     * 用户日常活动本地缓存，避免短时间内重复查询数据库
     * Key: userId:activityDate (例如: "123:2025-10-30")
     * Value: UserDailyActivityDO 或者占位符对象
     * 
     * 缓存策略：
     * 1. 每天定时清理前一天的缓存
     * 2. 避免同一用户同一天的重复数据库查询
     * 3. 减少并发请求时的数据库压力
     */
    private final ConcurrentHashMap<String, UserDailyActivityDO> userDailyActivityCache = new ConcurrentHashMap<>();
    
    /**
     * 缓存的日期，用于判断是否需要清理缓存
     */
    private volatile LocalDate cacheDate = LocalDate.now();

    // ========== 统计数据计算 ==========

    @Override
    @Transactional
    public boolean calculateUserActivityStats(LocalDate statDate) {
        try {
            log.info("[calculateUserActivityStats] 开始计算用户活跃度统计，日期：{}", statDate);
            
            // 计算DAU
            UserActivityStatsDO dauStats = calculateDauStats(statDate);
            if (dauStats != null) {
                userActivityStatsMapper.insertOrUpdateStats(dauStats);
            }
            
            // 如果是周末，计算WAU
            if (statDate.getDayOfWeek().getValue() == 7) { // 周日
                UserActivityStatsDO wauStats = calculateWauStats(statDate);
                if (wauStats != null) {
                    userActivityStatsMapper.insertOrUpdateStats(wauStats);
                }
            }
            
            // 如果是月末，计算MAU
            if (statDate.equals(statDate.withDayOfMonth(statDate.lengthOfMonth()))) {
                UserActivityStatsDO mauStats = calculateMauStats(statDate);
                if (mauStats != null) {
                    userActivityStatsMapper.insertOrUpdateStats(mauStats);
                }
            }
            
            log.info("[calculateUserActivityStats] 完成计算用户活跃度统计，日期：{}", statDate);
            return true;
        } catch (Exception e) {
            log.error("[calculateUserActivityStats] 计算用户活跃度统计失败，日期：{}", statDate, e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean batchCalculateUserActivityStats(LocalDate startDate, LocalDate endDate) {
        try {
            log.info("[batchCalculateUserActivityStats] 开始批量计算用户活跃度统计，日期范围：{} - {}", startDate, endDate);
            
            LocalDate currentDate = startDate;
            int successCount = 0;
            int totalCount = 0;
            
            while (!currentDate.isAfter(endDate)) {
                totalCount++;
                if (calculateUserActivityStats(currentDate)) {
                    successCount++;
                }
                currentDate = currentDate.plusDays(1);
            }
            
            log.info("[batchCalculateUserActivityStats] 批量计算完成，成功：{}/{}", successCount, totalCount);
            return successCount == totalCount;
        } catch (Exception e) {
            log.error("[batchCalculateUserActivityStats] 批量计算用户活跃度统计失败", e);
            return false;
        }
    }

    @Override
    @Transactional
    public boolean recalculateUserActivityStats(LocalDate statDate) {
        try {
            log.info("[recalculateUserActivityStats] 开始重新计算用户活跃度统计，日期：{}", statDate);
            
            // 删除已有的统计数据
            userActivityStatsMapper.delete(UserActivityStatsDO::getStatDate, statDate);
            
            // 重新计算
            return calculateUserActivityStats(statDate);
        } catch (Exception e) {
            log.error("[recalculateUserActivityStats] 重新计算用户活跃度统计失败，日期：{}", statDate, e);
            return false;
        }
    }

    @Override
    public UserActivityStatsDO calculateDauStats(LocalDate statDate) {
        try {
            log.debug("[calculateDauStats] 开始计算DAU统计，日期：{}", statDate);
            
            LocalDateTime startTime = statDate.atStartOfDay();
            LocalDateTime endTime = statDate.atTime(LocalTime.MAX);
            
            // 构建统计数据
            UserActivityStatsDO stats = UserActivityStatsDO.builder()
                    .statDate(statDate)
                    .statType(UserActivityStatsDO.STAT_TYPE_DAU)
                    .calculationStatus(UserActivityStatsDO.CALCULATION_STATUS_PROCESSING)
                    .calculationStartTime(LocalDateTime.now())
                    .build();
            
            // 计算基础活跃数据
            calculateBasicActivityStats(stats, startTime, endTime);
            
            // 计算登录统计
            calculateLoginStats(stats, startTime, endTime);
            
            // 计算用户类型统计
            calculateUserTypeStats(stats, startTime, endTime);
            
            // 计算设备统计
            calculateDeviceStats(stats, startTime, endTime);
            
            // 计算时间段统计
            calculateTimeSlotStats(stats, startTime, endTime);
            
            // 计算地域统计
            calculateRegionStats(stats, startTime, endTime);
            
            // 计算连续活跃统计
            calculateConsecutiveStats(stats, statDate);
            
            // 更新计算状态
            stats.setCalculationStatus(UserActivityStatsDO.CALCULATION_STATUS_COMPLETED);
            stats.setCalculationEndTime(LocalDateTime.now());
            stats.setCalculationDuration((int) ChronoUnit.SECONDS.between(stats.getCalculationStartTime(), stats.getCalculationEndTime()));
            
            log.debug("[calculateDauStats] 完成DAU统计计算，活跃用户数：{}", stats.getActiveUsers());
            return stats;
        } catch (Exception e) {
            log.error("[calculateDauStats] 计算DAU统计失败，日期：{}", statDate, e);
            return null;
        }
    }

    @Override
    public UserActivityStatsDO calculateWauStats(LocalDate statDate) {
        try {
            log.debug("[calculateWauStats] 开始计算WAU统计，日期：{}", statDate);
            
            // 计算周的开始和结束时间
            LocalDate weekStart = statDate.minusDays(6); // 周一到周日
            LocalDateTime startTime = weekStart.atStartOfDay();
            LocalDateTime endTime = statDate.atTime(LocalTime.MAX);
            
            UserActivityStatsDO stats = UserActivityStatsDO.builder()
                    .statDate(statDate)
                    .statType(UserActivityStatsDO.STAT_TYPE_WAU)
                    .calculationStatus(UserActivityStatsDO.CALCULATION_STATUS_PROCESSING)
                    .calculationStartTime(LocalDateTime.now())
                    .build();
            
            // 计算周活跃数据
            calculateBasicActivityStats(stats, startTime, endTime);
            calculateLoginStats(stats, startTime, endTime);
            calculateUserTypeStats(stats, startTime, endTime);
            calculateDeviceStats(stats, startTime, endTime);
            calculateTimeSlotStats(stats, startTime, endTime);
            calculateRegionStats(stats, startTime, endTime);
            calculateConsecutiveStats(stats, statDate);
            
            stats.setCalculationStatus(UserActivityStatsDO.CALCULATION_STATUS_COMPLETED);
            stats.setCalculationEndTime(LocalDateTime.now());
            stats.setCalculationDuration((int) ChronoUnit.SECONDS.between(stats.getCalculationStartTime(), stats.getCalculationEndTime()));
            
            log.debug("[calculateWauStats] 完成WAU统计计算，活跃用户数：{}", stats.getActiveUsers());
            return stats;
        } catch (Exception e) {
            log.error("[calculateWauStats] 计算WAU统计失败，日期：{}", statDate, e);
            return null;
        }
    }

    @Override
    public UserActivityStatsDO calculateMauStats(LocalDate statDate) {
        try {
            log.debug("[calculateMauStats] 开始计算MAU统计，日期：{}", statDate);
            
            // 计算月的开始和结束时间
            LocalDate monthStart = statDate.withDayOfMonth(1);
            LocalDateTime startTime = monthStart.atStartOfDay();
            LocalDateTime endTime = statDate.atTime(LocalTime.MAX);
            
            UserActivityStatsDO stats = UserActivityStatsDO.builder()
                    .statDate(statDate)
                    .statType(UserActivityStatsDO.STAT_TYPE_MAU)
                    .calculationStatus(UserActivityStatsDO.CALCULATION_STATUS_PROCESSING)
                    .calculationStartTime(LocalDateTime.now())
                    .build();
            
            // 计算月活跃数据
            calculateBasicActivityStats(stats, startTime, endTime);
            calculateLoginStats(stats, startTime, endTime);
            calculateUserTypeStats(stats, startTime, endTime);
            calculateDeviceStats(stats, startTime, endTime);
            calculateTimeSlotStats(stats, startTime, endTime);
            calculateRegionStats(stats, startTime, endTime);
            calculateConsecutiveStats(stats, statDate);
            
            stats.setCalculationStatus(UserActivityStatsDO.CALCULATION_STATUS_COMPLETED);
            stats.setCalculationEndTime(LocalDateTime.now());
            stats.setCalculationDuration((int) ChronoUnit.SECONDS.between(stats.getCalculationStartTime(), stats.getCalculationEndTime()));
            
            log.debug("[calculateMauStats] 完成MAU统计计算，活跃用户数：{}", stats.getActiveUsers());
            return stats;
        } catch (Exception e) {
            log.error("[calculateMauStats] 计算MAU统计失败，日期：{}", statDate, e);
            return null;
        }
    }

    // ========== 私有计算方法 ==========

    /**
     * 计算基础活跃统计
     */
    private void calculateBasicActivityStats(UserActivityStatsDO stats, LocalDateTime startTime, LocalDateTime endTime) {
        // 获取总用户数
        long totalUsers = adminUserMapper.selectCount();
        stats.setTotalUsers(totalUsers);
        
        // 获取活跃用户（有登录记录的用户）
        Set<Long> activeUserIds = getActiveUserIds(startTime, endTime);
        stats.setActiveUsers((long) activeUserIds.size());
        
        // 计算活跃率
        if (totalUsers > 0) {
            BigDecimal activityRate = BigDecimal.valueOf(activeUserIds.size())
                    .divide(BigDecimal.valueOf(totalUsers), 4, RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(100));
            stats.setActivityRate(activityRate);
        } else {
            stats.setActivityRate(BigDecimal.ZERO);
        }
        
        // 计算新用户数和回访用户数
        calculateNewAndReturningUsers(stats, activeUserIds, startTime, endTime);
    }

    /**
     * 获取活跃用户ID集合
     */
    private Set<Long> getActiveUserIds(LocalDateTime startTime, LocalDateTime endTime) {
        // 从登录日志获取活跃用户
        List<LoginLogDO> loginLogs = loginLogMapper.selectList(
                new LambdaQueryWrapperX<LoginLogDO>()
                        .between(LoginLogDO::getCreateTime, startTime, endTime)
                        .eq(LoginLogDO::getResult, LoginResultEnum.SUCCESS.getResult())
        );
        
        Set<Long> activeUserIds = loginLogs.stream()
                .map(LoginLogDO::getUserId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        
        // 注意：这里简化了实现，仅从登录日志获取活跃用户
        // 如果需要更精确的活跃用户统计，可以考虑其他数据源
        
        return activeUserIds;
    }

    /**
     * 计算新用户和回访用户
     */
    private void calculateNewAndReturningUsers(UserActivityStatsDO stats, Set<Long> activeUserIds, 
                                               LocalDateTime startTime, LocalDateTime endTime) {
        long newUsers = 0;
        long returningUsers = 0;
        
        for (Long userId : activeUserIds) {
            AdminUserDO user = adminUserMapper.selectById(userId);
            if (user != null) {
                // 判断是否为新用户（注册时间在统计时间范围内）
                if (user.getCreateTime().isAfter(startTime.minusDays(1)) && 
                    user.getCreateTime().isBefore(endTime.plusDays(1))) {
                    newUsers++;
                } else {
                    returningUsers++;
                }
            }
        }
        
        stats.setNewUsers(newUsers);
        stats.setReturningUsers(returningUsers);
    }

    /**
     * 计算登录统计
     */
    private void calculateLoginStats(UserActivityStatsDO stats, LocalDateTime startTime, LocalDateTime endTime) {
        List<LoginLogDO> successLoginLogs = loginLogMapper.selectList(
                new LambdaQueryWrapperX<LoginLogDO>()
                        .between(LoginLogDO::getCreateTime, startTime, endTime)
                        .eq(LoginLogDO::getResult, LoginResultEnum.SUCCESS.getResult())
        );
        
        // 登录用户数
        long loginUsers = successLoginLogs.stream()
                .map(LoginLogDO::getUserId)
                .filter(Objects::nonNull)
                .distinct()
                .count();
        stats.setLoginUsers(loginUsers);
        
        // 总登录次数
        stats.setLoginTimes((long) successLoginLogs.size());
        
        // 平均登录次数
        if (loginUsers > 0) {
            BigDecimal avgLoginTimes = BigDecimal.valueOf(successLoginLogs.size())
                    .divide(BigDecimal.valueOf(loginUsers), 2, RoundingMode.HALF_UP);
            stats.setAvgLoginTimes(avgLoginTimes);
        } else {
            stats.setAvgLoginTimes(BigDecimal.ZERO);
        }
    }

    /**
     * 计算用户类型统计
     */
    private void calculateUserTypeStats(UserActivityStatsDO stats, LocalDateTime startTime, LocalDateTime endTime) {
        Set<Long> activeUserIds = getActiveUserIds(startTime, endTime);
        
        long adminActiveUsers = 0;
        long memberActiveUsers = 0;
        
        for (Long userId : activeUserIds) {
            AdminUserDO user = adminUserMapper.selectById(userId);
            if (user != null) {
                // 根据用户类型分类（这里简化处理，所有用户都算作会员用户）
                // 如果需要区分管理员和会员，可以根据角色或其他字段来判断
                memberActiveUsers++;
            }
        }
        
        stats.setAdminActiveUsers(adminActiveUsers);
        stats.setMemberActiveUsers(memberActiveUsers);
    }

    /**
     * 计算设备统计
     */
    private void calculateDeviceStats(UserActivityStatsDO stats, LocalDateTime startTime, LocalDateTime endTime) {
        List<LoginLogDO> loginLogs = loginLogMapper.selectList(
                new LambdaQueryWrapperX<LoginLogDO>()
                        .between(LoginLogDO::getCreateTime, startTime, endTime)
                        .eq(LoginLogDO::getResult, LoginResultEnum.SUCCESS.getResult())
        );
        
        Set<Long> pcUsers = new HashSet<>();
        Set<Long> mobileUsers = new HashSet<>();
        Set<Long> webUsers = new HashSet<>();
        
        for (LoginLogDO log : loginLogs) {
            if (log.getUserId() != null && StrUtil.isNotBlank(log.getUserAgent())) {
                String userAgent = log.getUserAgent().toLowerCase();
                if (userAgent.contains("mobile") || userAgent.contains("android") || userAgent.contains("iphone")) {
                    mobileUsers.add(log.getUserId());
                } else if (userAgent.contains("windows") || userAgent.contains("mac") || userAgent.contains("linux")) {
                    pcUsers.add(log.getUserId());
                } else {
                    webUsers.add(log.getUserId());
                }
            }
        }
        
        stats.setPcUsers((long) pcUsers.size());
        stats.setMobileUsers((long) mobileUsers.size());
        stats.setWebUsers((long) webUsers.size());
    }

    /**
     * 计算时间段统计
     */
    private void calculateTimeSlotStats(UserActivityStatsDO stats, LocalDateTime startTime, LocalDateTime endTime) {
        List<LoginLogDO> loginLogs = loginLogMapper.selectList(
                new LambdaQueryWrapperX<LoginLogDO>()
                        .between(LoginLogDO::getCreateTime, startTime, endTime)
                        .eq(LoginLogDO::getResult, LoginResultEnum.SUCCESS.getResult())
        );
        
        Set<Long> morningUsers = new HashSet<>();
        Set<Long> afternoonUsers = new HashSet<>();
        Set<Long> eveningUsers = new HashSet<>();
        Set<Long> nightUsers = new HashSet<>();
        
        for (LoginLogDO log : loginLogs) {
            if (log.getUserId() != null && log.getCreateTime() != null) {
                int hour = log.getCreateTime().getHour();
                if (hour >= 6 && hour < 12) {
                    morningUsers.add(log.getUserId());
                } else if (hour >= 12 && hour < 18) {
                    afternoonUsers.add(log.getUserId());
                } else if (hour >= 18 && hour < 24) {
                    eveningUsers.add(log.getUserId());
                } else {
                    nightUsers.add(log.getUserId());
                }
            }
        }
        
        stats.setMorningUsers((long) morningUsers.size());
        stats.setAfternoonUsers((long) afternoonUsers.size());
        stats.setEveningUsers((long) eveningUsers.size());
        stats.setNightUsers((long) nightUsers.size());
    }

    /**
     * 计算地域统计
     */
    private void calculateRegionStats(UserActivityStatsDO stats, LocalDateTime startTime, LocalDateTime endTime) {
        List<LoginLogDO> loginLogs = loginLogMapper.selectList(
                new LambdaQueryWrapperX<LoginLogDO>()
                        .between(LoginLogDO::getCreateTime, startTime, endTime)
                        .eq(LoginLogDO::getResult, LoginResultEnum.SUCCESS.getResult())
        );
        
        // 统计IP地址对应的地域（这里简化处理，实际需要IP地址库）
        Map<String, Set<Long>> regionUserMap = new HashMap<>();
        
        for (LoginLogDO log : loginLogs) {
            if (log.getUserId() != null && StrUtil.isNotBlank(log.getUserIp())) {
                String region = getRegionByIp(log.getUserIp()); // 需要实现IP地址解析
                regionUserMap.computeIfAbsent(region, k -> new HashSet<>()).add(log.getUserId());
            }
        }
        
        // 获取前3个地区
        List<Map.Entry<String, Set<Long>>> topRegions = regionUserMap.entrySet().stream()
                .sorted((e1, e2) -> Integer.compare(e2.getValue().size(), e1.getValue().size()))
                .limit(3)
                .collect(Collectors.toList());
        
        if (topRegions.size() > 0) {
            stats.setTopRegion1(topRegions.get(0).getKey());
            stats.setTopRegion1Users((long) topRegions.get(0).getValue().size());
        }
        if (topRegions.size() > 1) {
            stats.setTopRegion2(topRegions.get(1).getKey());
            stats.setTopRegion2Users((long) topRegions.get(1).getValue().size());
        }
        if (topRegions.size() > 2) {
            stats.setTopRegion3(topRegions.get(2).getKey());
            stats.setTopRegion3Users((long) topRegions.get(2).getValue().size());
        }
    }

    /**
     * 根据IP地址获取地域信息（简化实现）
     */
    private String getRegionByIp(String ip) {
        // 这里需要集成IP地址库，暂时返回默认值
        if (ip.startsWith("192.168.") || ip.startsWith("10.") || ip.startsWith("172.")) {
            return "本地网络";
        }
        return "未知地区";
    }

    /**
     * 计算连续活跃统计
     */
    private void calculateConsecutiveStats(UserActivityStatsDO stats, LocalDate statDate) {
        // 获取用户每日活跃记录
        List<UserDailyActivityDO> dailyActivities = userDailyActivityMapper.selectByDateRange(
                statDate.minusDays(30), statDate
        );
        
        Map<Integer, Set<Long>> consecutiveUserMap = new HashMap<>();
        consecutiveUserMap.put(1, new HashSet<>());
        consecutiveUserMap.put(3, new HashSet<>());
        consecutiveUserMap.put(7, new HashSet<>());
        consecutiveUserMap.put(15, new HashSet<>());
        
        // 按用户分组统计连续活跃天数
        Map<Long, List<UserDailyActivityDO>> userActivityMap = dailyActivities.stream()
                .filter(activity -> activity.getIsActive())
                .collect(Collectors.groupingBy(UserDailyActivityDO::getUserId));
        
        for (Map.Entry<Long, List<UserDailyActivityDO>> entry : userActivityMap.entrySet()) {
            Long userId = entry.getKey();
            List<UserDailyActivityDO> activities = entry.getValue();
            
            // 计算连续活跃天数
            int consecutiveDays = calculateConsecutiveDays(activities, statDate);
            
            if (consecutiveDays >= 1) consecutiveUserMap.get(1).add(userId);
            if (consecutiveDays >= 3) consecutiveUserMap.get(3).add(userId);
            if (consecutiveDays >= 7) consecutiveUserMap.get(7).add(userId);
            if (consecutiveDays >= 15) consecutiveUserMap.get(15).add(userId);
        }
        
        stats.setConsecutive1Day((long) consecutiveUserMap.get(1).size());
        stats.setConsecutive3Days((long) consecutiveUserMap.get(3).size());
        stats.setConsecutive7Days((long) consecutiveUserMap.get(7).size());
        stats.setConsecutive15Days((long) consecutiveUserMap.get(15).size());
    }

    /**
     * 计算用户连续活跃天数
     */
    private int calculateConsecutiveDays(List<UserDailyActivityDO> activities, LocalDate endDate) {
        if (CollUtil.isEmpty(activities)) {
            return 0;
        }
        
        // 按日期排序
        activities.sort(Comparator.comparing(UserDailyActivityDO::getActivityDate));
        
        int consecutiveDays = 0;
        LocalDate currentDate = endDate;
        
        // 从结束日期往前计算连续天数
        for (int i = activities.size() - 1; i >= 0; i--) {
            UserDailyActivityDO activity = activities.get(i);
            if (activity.getActivityDate().equals(currentDate)) {
                consecutiveDays++;
                currentDate = currentDate.minusDays(1);
            } else if (activity.getActivityDate().isBefore(currentDate)) {
                break;
            }
        }
        
        return consecutiveDays;
    }

    // ========== 统计数据查询 ==========

    @Override
    public PageResult<UserActivityStatsDO> getUserActivityStatsPage(LocalDate startDate, LocalDate endDate,
                                                                    Integer statType, Integer calculationStatus,
                                                                    int pageNo, int pageSize) {
        return userActivityStatsMapper.selectPage(startDate, endDate, statType, calculationStatus, pageNo, pageSize);
    }

    @Override
    public Map<String, Object> getUserActivityStatsOverview(LocalDate startDate, LocalDate endDate) {
        Map<String, Object> overview = new HashMap<>();
        
        // 获取DAU数据
        List<UserActivityStatsDO> dauList = userActivityStatsMapper.selectByDateRangeAndType(
                startDate, endDate, UserActivityStatsDO.STAT_TYPE_DAU);
        
        if (CollUtil.isNotEmpty(dauList)) {
            // 计算平均DAU
            double avgDau = dauList.stream()
                    .mapToLong(UserActivityStatsDO::getActiveUsers)
                    .average()
                    .orElse(0.0);
            overview.put("avgDau", Math.round(avgDau));
            
            // 最高DAU
            long maxDau = dauList.stream()
                    .mapToLong(UserActivityStatsDO::getActiveUsers)
                    .max()
                    .orElse(0L);
            overview.put("maxDau", maxDau);
            
            // 最低DAU
            long minDau = dauList.stream()
                    .mapToLong(UserActivityStatsDO::getActiveUsers)
                    .min()
                    .orElse(0L);
            overview.put("minDau", minDau);
            
            // 总活跃用户数（去重）
            Set<Long> allActiveUsers = new HashSet<>();
            dauList.forEach(stats -> allActiveUsers.add(stats.getActiveUsers()));
            overview.put("totalActiveUsers", allActiveUsers.size());
        }
        
        return overview;
    }

    @Override
    public List<UserActivityStatsDO> getUserActivityTrend(LocalDate startDate, LocalDate endDate, Integer statType) {
        return userActivityStatsMapper.selectActivityTrend(startDate, endDate, statType);
    }

    @Override
    public UserActivityStatsDO getUserActivityStatsDetail(LocalDate statDate, Integer statType) {
        return userActivityStatsMapper.selectByDateAndType(statDate, statType);
    }

    @Override
    public List<UserActivityStatsDO> getLatestUserActivityStats(Integer statType, Integer limit) {
        return userActivityStatsMapper.selectLatestStats(statType, limit);
    }

    // ========== 用户活跃明细 ==========

    @Override
    @Transactional
    public UserDailyActivityDO recordUserDailyActivity(Long userId, LocalDate activityDate) {
        // 检查是否需要清理缓存（日期变更时）
        cleanCacheIfDateChanged(activityDate);
        
        // 构建缓存key
        String cacheKey = buildCacheKey(userId, activityDate);
        
        // 先从缓存获取，避免重复查询数据库
        UserDailyActivityDO cached = userDailyActivityCache.get(cacheKey);
        if (cached != null) {
            log.debug("[recordUserDailyActivity] 从缓存获取记录: userId={}, activityDate={}", userId, activityDate);
            return cached;
        }
        
        // 缓存未命中，查询数据库
        UserDailyActivityDO existing = userDailyActivityMapper.selectByUserIdAndDate(userId, activityDate);
        if (existing != null) {
            // 将查询结果放入缓存
            userDailyActivityCache.put(cacheKey, existing);
            log.debug("[recordUserDailyActivity] 查询到已存在记录并缓存: userId={}, activityDate={}", userId, activityDate);
            return existing;
        }
        
        // 创建新记录
        AdminUserDO user = adminUserMapper.selectById(userId);
        if (user == null) {
            return null;
        }
        
        UserDailyActivityDO dailyActivity = UserDailyActivityDO.builder()
                .userId(userId)
                .username(user.getUsername())
                .userType(UserDailyActivityDO.USER_TYPE_MEMBER)
                .activityDate(activityDate)
                .isActive(true)
                .isNewUser(user.getCreateTime().toLocalDate().equals(activityDate))
                .loginCount(0)
                .onlineDuration(0)
                .pageViews(0)
                .apiCalls(0)
                .featureUsageCount(0)
                .consecutiveDays(1)
                .totalActiveDays(1)
                .build();
        
        try {
            userDailyActivityMapper.insert(dailyActivity);
            // 插入成功后，放入缓存
            userDailyActivityCache.put(cacheKey, dailyActivity);
            log.debug("[recordUserDailyActivity] 创建新记录并缓存: userId={}, activityDate={}", userId, activityDate);
            return dailyActivity;
        } catch (org.springframework.dao.DuplicateKeyException e) {
            // 如果发生重复键异常，说明在并发情况下另一个线程已经插入了记录
            // 重新查询并返回现有记录
            log.debug("[recordUserDailyActivity] 检测到重复键异常，重新查询现有记录: userId={}, activityDate={}", userId, activityDate);
            UserDailyActivityDO retryRecord = userDailyActivityMapper.selectByUserIdAndDate(userId, activityDate);
            if (retryRecord != null) {
                // 将查询结果放入缓存
                userDailyActivityCache.put(cacheKey, retryRecord);
                return retryRecord;
            }
            // 如果仍然查询不到，说明可能有其他问题，重新抛出异常
            throw e;
        }
    }
    
    /**
     * 构建缓存Key
     */
    private String buildCacheKey(Long userId, LocalDate activityDate) {
        return userId + ":" + activityDate;
    }
    
    /**
     * 如果日期变更，清理旧的缓存
     */
    private void cleanCacheIfDateChanged(LocalDate currentDate) {
        if (!currentDate.equals(cacheDate)) {
            synchronized (this) {
                if (!currentDate.equals(cacheDate)) {
                    log.info("[cleanCacheIfDateChanged] 日期变更，清理用户活动缓存: 旧日期={}, 新日期={}, 缓存大小={}", 
                            cacheDate, currentDate, userDailyActivityCache.size());
                    userDailyActivityCache.clear();
                    cacheDate = currentDate;
                }
            }
        }
    }
    
    /**
     * 定时清理缓存（每天凌晨1点执行）
     * 防止缓存无限增长，即使日期检查失败也能清理
     */
    @Scheduled(cron = "0 0 1 * * ?")
    public void scheduledCleanCache() {
        try {
            LocalDate today = LocalDate.now();
            if (userDailyActivityCache.size() > 0) {
                log.info("[scheduledCleanCache] 定时清理用户活动缓存: 当前日期={}, 缓存大小={}", 
                        today, userDailyActivityCache.size());
                userDailyActivityCache.clear();
                cacheDate = today;
            }
        } catch (Exception e) {
            log.error("[scheduledCleanCache] 定时清理缓存失败", e);
        }
    }
    
    /**
     * 获取缓存统计信息（用于监控）
     */
    public Map<String, Object> getCacheStats() {
        Map<String, Object> stats = new HashMap<>();
        stats.put("cacheSize", userDailyActivityCache.size());
        stats.put("cacheDate", cacheDate);
        stats.put("currentDate", LocalDate.now());
        return stats;
    }

    @Override
    @Transactional
    public boolean updateUserDailyActivity(UserDailyActivityDO userDailyActivity) {
        return userDailyActivityMapper.updateById(userDailyActivity) > 0;
    }

    @Override
    public PageResult<UserDailyActivityDO> getUserDailyActivityPage(LocalDate startDate, LocalDate endDate,
                                                                    Long userId, Boolean isActive, Integer userType,
                                                                    int pageNo, int pageSize) {
        return userDailyActivityMapper.selectPage(startDate, endDate, userId, isActive, userType, pageNo, pageSize);
    }

    @Override
    public UserDailyActivityDO getUserDailyActivity(Long id) {
        return userDailyActivityMapper.selectById(id);
    }

    @Override
    public List<UserDailyActivityDO> getActiveUsersByDate(LocalDate activityDate) {
        return userDailyActivityMapper.selectActiveUsersByDate(activityDate);
    }

    @Override
    public List<UserDailyActivityDO> getActiveUsers(LocalDate activityDate, LocalDate startDate, LocalDate endDate, Integer userType, Integer limit) {
        return userDailyActivityMapper.selectActiveUsers(activityDate, startDate, endDate, userType, limit);
    }

    @Override
    public List<UserDailyActivityDO> getHighActiveUsers(LocalDate startDate, LocalDate endDate, Integer minLoginDays) {
        // 使用endDate作为查询日期，minLoginDays作为最小功能使用次数
        return userDailyActivityMapper.selectHighActiveUsers(endDate, minLoginDays);
    }

    @Override
    public List<UserDailyActivityDO> getReturnUsers(LocalDate startDate, LocalDate endDate, Integer inactiveDays) {
        // 使用传入的日期范围查询回流用户
        return userDailyActivityMapper.selectReturnUsers(endDate, startDate, endDate);
    }

    // ========== 数据分析 ==========

    @Override
    public Map<String, Object> analyzeActivityChange(LocalDate startDate, LocalDate endDate, LocalDate compareStartDate, LocalDate compareEndDate) {
        Map<String, Object> analysis = new HashMap<>();
        
        List<UserActivityStatsDO> dauList = userActivityStatsMapper.selectByDateRangeAndType(
                startDate, endDate, UserActivityStatsDO.STAT_TYPE_DAU);
        
        if (CollUtil.isNotEmpty(dauList) && dauList.size() > 1) {
            // 计算增长率
            UserActivityStatsDO first = dauList.get(0);
            UserActivityStatsDO last = dauList.get(dauList.size() - 1);
            
            if (first.getActiveUsers() > 0) {
                double growthRate = ((double) (last.getActiveUsers() - first.getActiveUsers()) / first.getActiveUsers()) * 100;
                analysis.put("growthRate", Math.round(growthRate * 100) / 100.0);
            }
            
            // 计算波动性
            double avg = dauList.stream().mapToLong(UserActivityStatsDO::getActiveUsers).average().orElse(0.0);
            double variance = dauList.stream()
                    .mapToDouble(stats -> Math.pow(stats.getActiveUsers() - avg, 2))
                    .average()
                    .orElse(0.0);
            analysis.put("volatility", Math.round(Math.sqrt(variance) * 100) / 100.0);
        }
        
        return analysis;
    }

    @Override
    public Map<String, Object> analyzeUserRetention(LocalDate startDate, LocalDate endDate, Integer retentionDays) {
        Map<String, Object> analysis = new HashMap<>();
        
        // 获取新用户数据
        List<UserActivityStatsDO> dauList = userActivityStatsMapper.selectByDateRangeAndType(
                startDate, endDate, UserActivityStatsDO.STAT_TYPE_DAU);
        
        long totalNewUsers = dauList.stream()
                .mapToLong(UserActivityStatsDO::getNewUsers)
                .sum();
        
        long totalReturningUsers = dauList.stream()
                .mapToLong(UserActivityStatsDO::getReturningUsers)
                .sum();
        
        analysis.put("totalNewUsers", totalNewUsers);
        analysis.put("totalReturningUsers", totalReturningUsers);
        
        if (totalNewUsers + totalReturningUsers > 0) {
            double retentionRate = ((double) totalReturningUsers / (totalNewUsers + totalReturningUsers)) * 100;
            analysis.put("retentionRate", Math.round(retentionRate * 100) / 100.0);
        }
        
        return analysis;
    }

    @Override
    public Map<String, Object> analyzeUserBehaviorPattern(LocalDate startDate, LocalDate endDate) {
        Map<String, Object> analysis = new HashMap<>();
        
        List<UserActivityStatsDO> dauList = userActivityStatsMapper.selectByDateRangeAndType(
                startDate, endDate, UserActivityStatsDO.STAT_TYPE_DAU);
        
        if (CollUtil.isNotEmpty(dauList)) {
            // 时间段偏好分析
            long totalMorning = dauList.stream().mapToLong(UserActivityStatsDO::getMorningUsers).sum();
            long totalAfternoon = dauList.stream().mapToLong(UserActivityStatsDO::getAfternoonUsers).sum();
            long totalEvening = dauList.stream().mapToLong(UserActivityStatsDO::getEveningUsers).sum();
            long totalNight = dauList.stream().mapToLong(UserActivityStatsDO::getNightUsers).sum();
            
            Map<String, Long> timeSlotPreference = new HashMap<>();
            timeSlotPreference.put("morning", totalMorning);
            timeSlotPreference.put("afternoon", totalAfternoon);
            timeSlotPreference.put("evening", totalEvening);
            timeSlotPreference.put("night", totalNight);
            analysis.put("timeSlotPreference", timeSlotPreference);
            
            // 设备偏好分析
            long totalPc = dauList.stream().mapToLong(UserActivityStatsDO::getPcUsers).sum();
            long totalMobile = dauList.stream().mapToLong(UserActivityStatsDO::getMobileUsers).sum();
            long totalWeb = dauList.stream().mapToLong(UserActivityStatsDO::getWebUsers).sum();
            
            Map<String, Long> devicePreference = new HashMap<>();
            devicePreference.put("pc", totalPc);
            devicePreference.put("mobile", totalMobile);
            devicePreference.put("web", totalWeb);
            analysis.put("devicePreference", devicePreference);
        }
        
        return analysis;
    }

    // ========== 数据管理 ==========

    @Override
    @Transactional
    public int deleteExpiredStats(LocalDate beforeDate) {
        return userActivityStatsMapper.deleteExpiredData(beforeDate);
    }

    @Override
    @Transactional
    public int deleteExpiredDailyActivity(LocalDate beforeDate) {
        return userDailyActivityMapper.deleteExpiredData(beforeDate);
    }

    @Override
    public Map<String, Object> getCalculationProgress(LocalDate startDate, LocalDate endDate) {
        Map<String, Object> progress = new HashMap<>();
        
        long totalDays = ChronoUnit.DAYS.between(startDate, endDate) + 1;
        long completedDays = userActivityStatsMapper.countCompletedByDate(startDate);
        
        progress.put("totalDays", totalDays);
        progress.put("completedDays", completedDays);
        progress.put("progressRate", totalDays > 0 ? (double) completedDays / totalDays * 100 : 0);
        
        return progress;
    }

    @Override
    public Map<String, Object> checkDataIntegrity(LocalDate startDate, LocalDate endDate) {
        Map<String, Object> integrity = new HashMap<>();
        
        List<String> issues = new ArrayList<>();
        
        // 检查是否有缺失的统计数据
        LocalDate currentDate = startDate;
        while (!currentDate.isAfter(endDate)) {
            long count = userActivityStatsMapper.countByDate(currentDate);
            if (count == 0) {
                issues.add("缺失日期 " + currentDate + " 的统计数据");
            }
            currentDate = currentDate.plusDays(1);
        }
        
        // 检查是否有计算失败的数据
        long failedCount = userActivityStatsMapper.countByCalculationStatus(
                UserActivityStatsDO.CALCULATION_STATUS_FAILED);
        if (failedCount > 0) {
            issues.add("有 " + failedCount + " 条计算失败的记录");
        }
        
        integrity.put("issues", issues);
        integrity.put("isHealthy", issues.isEmpty());
        
        return integrity;
    }
}